home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994 June: Reference Library / Dev.CD Jun 94.toast / Periodicals / develop / develop Issue 15 / develop 15 code / Symmetry & Tiles / Tiler Code / Tilings.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-09-13  |  13.0 KB  |  660 lines  |  [TEXT/KAHL]

  1. #include     <graphics libraries.h>
  2. #include     <graphics toolbox.h>
  3. #include     <qd library.h>
  4. #include     <offscreen library.h>
  5. #include    "TileConstants.h"
  6. #include    "TileProtos.h"
  7.  
  8. #include     "Group1.h"
  9. #include     "Group2.h"
  10.  
  11. //---------------------------------------
  12. //    Tile globals
  13. gxPoint            gOrigin = {ff(0), ff(0)};
  14. gxColorSet        gColors;
  15. offscreen        gOffScreen;
  16. gxShape            gTileShape,    // The unit cell (transforms of gDragger contours)
  17.                 gGridShape,    // A rect filled with the pattern
  18.                 gBGShape;    // A rect filled with the background color
  19.  
  20. gxShape            gOpShapes[kNumOpShapes];    // Group of shapes to show the symmetry 
  21.                                             // operations
  22.  
  23. dragger            gDragger;    // A record containing a manipulable contour & associated stuff
  24. gxPatternRecord    gPattern;
  25.  
  26. Boolean            gContRedraw = false, gKeepClosed = true; // State Booleans
  27. long            gSymmetry = p1; // The currently chosen symmetry group
  28.  
  29. // ========= Prototypes ==========================================
  30.  
  31. // Prototype of a function in utils.c we use
  32. long     labs(long i);
  33.  
  34. // Prototypes for routines private to this file
  35. long     AddEdgePoint(gxPoint *clickPt, long prevIndex);
  36. Boolean    RemoveEdgePoint(long index);
  37. Boolean IsConstrainedPoint(long ptIndex);
  38.  
  39. void     RemakeOpShapes(Boolean useDefaults, long newSymmetry);
  40.  
  41. void    TrackDraggerPoint(long dragPtIndex, gxPoint *startPt);
  42. Boolean OpHit(gxPoint *clickPt);
  43.  
  44. void     DrawDraggable(void);
  45. void     EraseDraggable(void);
  46. void    DrawOpShapes(void);
  47.  
  48. void     SetLinePoints(gxShape theLine, gxPoint *start, gxPoint *end);
  49.  
  50. //===================================================================
  51.  
  52. // Adds a point to the global snake
  53. long AddEdgePoint(gxPoint *clickPt, long prevIndex)
  54. {
  55.     gxPoint    newPt[] = {1L, 1L, ff(0), ff(0)};
  56.     
  57.     // put the new point in newPt poly and add it to the snake
  58.     newPt[1] = *clickPt;
  59.     GXSetPolygonParts(gDragger.snake1, prevIndex + 1, 0, 
  60.                     (gxPolygons *)newPt, gxBreakNeitherEdit);
  61.     
  62.     switch(gSymmetry)
  63.     {
  64.         case p1:
  65.             p1_AddedDragPoint(prevIndex);
  66.             break;
  67.         
  68.         case pg:
  69.             pg_AddedDragPoint(prevIndex);
  70.             break;
  71.  
  72.         case pm:
  73.             break;
  74.  
  75.         case cm:
  76.             break;
  77.  
  78.         case p2:
  79.             break;
  80.         
  81.         default:
  82.             break;
  83.     }
  84.     return(prevIndex + 1);
  85. }
  86.  
  87. // Removes a point
  88. Boolean RemoveEdgePoint(long index)
  89. {
  90.     Boolean rslt = false;
  91.     
  92.     // If there are only 2 points, or if constraints are active and 
  93.     // it's a constrained point, then don't remove it
  94.     if( (GXCountShapePoints(gDragger.snake1, 0) < 3) || IsConstrainedPoint(index) ||
  95.         index == 1 )  // BUG: if index is one, GXSetPolygonParts deletes the entire contour
  96.         SysBeep(10);
  97.     else    // Remove the point
  98.     {
  99.         long    geo[25];
  100.         
  101.         GXSetPolygonParts(gDragger.snake1, index, 1, gxSetToNil, gxBreakNeitherEdit);
  102.         
  103.         switch(gSymmetry)
  104.         {
  105.             case p1:
  106.                 p1_RemovedDragPoint(index);
  107.                 break;
  108.             
  109.             case pg:
  110.                 pg_RemovedDragPoint(index);
  111.                 break;
  112.     
  113.             case pm:
  114.                 break;
  115.     
  116.             case cm:
  117.                 break;
  118.     
  119.             case p2:
  120.                 break;
  121.             
  122.             default:
  123.                 break;
  124.         }
  125.         rslt = true;
  126.     }
  127.     return rslt;
  128. }
  129.  
  130. Boolean IsConstrainedPoint(long ptIndex)
  131. {
  132.     Boolean    isConstrained = false;
  133.     
  134.     if(gKeepClosed)
  135.     {
  136.         switch(gSymmetry)
  137.         {
  138.             case p1:
  139.                 isConstrained = p1_IsVectorPoint(ptIndex);
  140.                 break;
  141.             
  142.             case pg:
  143.                 isConstrained = pg_IsVectorPoint(ptIndex);
  144.                 break;
  145.     
  146.             case pm:
  147.                 break;
  148.     
  149.             case cm:
  150.                 break;
  151.     
  152.             case p2:
  153.                 break;
  154.             
  155.             default:
  156.                 break;
  157.         }
  158.     }
  159.     return isConstrained;
  160. }
  161.  
  162. // returns true if something changed: the shell will update the window
  163. Boolean TileClick(gxPoint *clickPt, short optDown)
  164. {
  165.     gxHitTestInfo    hitStats;
  166.     gxTransform    tileXForm;
  167.     long        dragPtIndex = -1;
  168.     gxShape        dragShape;
  169.     
  170.     // Check for a dragger hit
  171.     tileXForm = GXGetShapeTransform(gDragger.snake1);
  172.     GXIgnoreGraphicsNotice(attributes_already_set);
  173.     GXSetTransformHitTest(tileXForm, gxGeometryPart, kHitTolerance);
  174.     GXPopGraphicsNotice();
  175.     
  176.     // If snake was hit, do further tests to find out what
  177.     if(GXHitTestShape(gDragger.snake1, clickPt, &hitStats))
  178.     {
  179.         // check for hit on a control point
  180.         GXSetTransformHitTest(tileXForm, gxCornerPointPart, kHitTolerance);
  181.         if(GXHitTestShape(gDragger.snake1, clickPt, &hitStats))
  182.         {
  183.             // if the option key is down, remove the point
  184.             // and change the pattern
  185.             if(optDown)
  186.             {
  187.                 if(RemoveEdgePoint(hitStats.index))
  188.                     RemakeTile();
  189.             }
  190.             
  191.             // else drag the point if it's not constrained
  192.             else
  193.             {
  194.                 if(IsConstrainedPoint(hitStats.index))
  195.                     SysBeep(10);
  196.                 else
  197.                 {
  198.                     dragShape = gDragger.snake1;
  199.                     dragPtIndex = hitStats.index;
  200.                 }
  201.             }
  202.         }
  203.         else    // Not a control point, prob'ly an edge
  204.         {
  205.             // check for hit on an edge: if so need to add a point
  206.             GXSetTransformHitTest(tileXForm, gxEdgePart, kHitTolerance);
  207.             if(GXHitTestShape(gDragger.snake1, clickPt, &hitStats))
  208.             {
  209.                 dragShape = gDragger.snake1;
  210.                 dragPtIndex = AddEdgePoint(clickPt, hitStats.index);
  211.             }
  212.         }
  213.         
  214.         // If a draggable hit, drag it
  215.         if(dragPtIndex > 0)
  216.             TrackDraggerPoint(dragPtIndex, clickPt);
  217.         return true;
  218.     }
  219.     
  220.     // Not a dragger hit, try the op shapes
  221.     return OpHit(clickPt);
  222. }
  223.  
  224. void TrackDraggerPoint(long dragPtIndex, gxPoint *startPt)
  225. {
  226.     gxPoint        pt, lastPt = *startPt;
  227.     
  228.     // Get current mouse
  229.     GXGetViewPortMouse(0, &pt); // 0 means use default viewPort
  230.     
  231.     // Follow the drag around
  232.     while(StillDown())
  233.     {
  234.         GXGetViewPortMouse(0, &pt);
  235.         if(pt.x != lastPt.x || pt.y != lastPt.y)
  236.         {
  237.             // Erase
  238.             if(!gContRedraw)
  239.                 EraseDraggable();
  240.             
  241.             // Set new point
  242.             GXSetShapePoints(gDragger.snake1, dragPtIndex, 1, &pt);
  243.             
  244.             if(gContRedraw)
  245.             {
  246.                 // Reset pattern and draw it right now
  247.                 RemakeTile();
  248.                   GXDrawShape(gOffScreen.draw);
  249.             }
  250.               else
  251.               {
  252.                   // Just draw the dragger
  253.                   DrawDraggable();
  254.               }
  255.           }
  256.           lastPt = pt;
  257.     }
  258.  
  259.     // Update pattern if we haven't yet
  260.      if(!gContRedraw)
  261.         RemakeTile();
  262. }    
  263.  
  264. // returns true if something was hit so the shell will update the window
  265. Boolean OpHit(gxPoint *clickPt)
  266. {
  267.     Boolean goodHit = false;
  268.     
  269.     switch(gSymmetry)
  270.     {
  271.         case p1:
  272.             goodHit =  p1_OpShapeHit(clickPt);
  273.             break;
  274.         
  275.         case pg:
  276.             goodHit =  pg_OpShapeHit(clickPt);
  277.             break;
  278.  
  279.         case pm:
  280.             goodHit =  pm_OpShapeHit(clickPt);
  281.             break;
  282.  
  283.         case cm:
  284.             goodHit =  cm_OpShapeHit(clickPt);
  285.             break;
  286.  
  287.         case p2:
  288.             goodHit =  p2_OpShapeHit(clickPt);
  289.             break;
  290.         
  291.         default:
  292.             goodHit = false;
  293.             break;
  294.     }
  295.     return goodHit;
  296. }
  297.  
  298. // Set up the default OpShapes and dragger for the symmetry
  299. void DefaultTileAndPattern()
  300. {
  301.     RemakeOpShapes(true, gSymmetry);
  302.     
  303.     switch(gSymmetry)
  304.     {
  305.         case p1:
  306.             p1_SetDefaultDragger(gDragger.snake1);
  307.             p1_ChangeLattice();
  308.             break;
  309.  
  310.         case pg:
  311.             pg_SetDefaultDragger(gDragger.snake1);
  312.             pg_ChangeLattice();
  313.             break;
  314.         
  315.         case pm:
  316.             pm_SetDefaultDragger(gDragger.snake1);
  317.             pm_ChangeLattice();
  318.             break;
  319.  
  320.         case cm:
  321.             cm_SetDefaultDragger(gDragger.snake1);
  322.             cm_ChangeLattice();
  323.             break;
  324.  
  325.         case p2:
  326.             p2_SetDefaultDragger(gDragger.snake1);
  327.             p2_ChangeLattice();
  328.             break;
  329.  
  330.         default:
  331.             break;
  332.     }
  333.     if(gKeepClosed)
  334.         AttachConstraints();
  335. }                    
  336.  
  337. void AttachConstraints(void)
  338. {
  339.     // Just call the right routine to do the work
  340.     switch(gSymmetry)
  341.     {
  342.         case p1:
  343.             p1_AttachConstraints();
  344.             break;
  345.             
  346.          case pg:
  347.             pg_AttachConstraints();
  348.             break;
  349.             
  350.          case pm:
  351.             break;
  352.             
  353.         case cm:
  354.             break;
  355.         
  356.         case p2:
  357.             break;
  358.  
  359.         default:
  360.             break;
  361.     }
  362. }
  363.  
  364. // Called either to set the default shapes when resetting the tile, or when 
  365. // the symmetry has changed, to convert one lattice type to another.
  366. // Currently always uses defaults.
  367. void RemakeOpShapes(Boolean useDefaults, long newSymmetry)
  368. {
  369.     // Kill old shapes
  370.     DisposeOpShapes();
  371.     
  372.     // Make the right new ones
  373.     switch(newSymmetry)
  374.     {
  375.         case p1:    // two translate shapes
  376.             p1_RemakeOpShapes(useDefaults);
  377.             break;
  378.             
  379.          case pg:    // two glide lines
  380.             pg_RemakeOpShapes(useDefaults);
  381.             break;
  382.             
  383.          case pm:    // two mirror lines
  384.             pm_RemakeOpShapes(useDefaults);
  385.             break;
  386.             
  387.         case cm:    // a mirror and a glide
  388.             cm_RemakeOpShapes(useDefaults);
  389.             break;
  390.         
  391.         case p2:    // 3 ovals
  392.             p2_RemakeOpShapes(useDefaults);
  393.             break;
  394.  
  395.         default:
  396.             break;
  397.     }
  398. }
  399.     
  400. // Reset the tile shape according to the dragger
  401. void RemakeTile(void)
  402. {
  403.     // Copy geometry into tile to start
  404.     GXSetShapeGeometry(gTileShape, gDragger.snake1);
  405.     
  406.     // Repeat the geometry as needed for the symmetry
  407.     switch(gSymmetry)
  408.     {
  409.         case p1:    // No repeats
  410.             break;
  411.         
  412.         case pg:    // Glide reflect
  413.             pg_BuildCellShape(gDragger.snake1);
  414.             break;
  415.         
  416.         case pm:
  417.             pm_BuildCellShape(gDragger.snake1);
  418.             break;
  419.         
  420.         case cm:
  421.             cm_BuildCellShape(gDragger.snake1);
  422.             break;
  423.         
  424.         case p2:
  425.             p2_BuildCellShape(gDragger.snake1);
  426.             break;
  427.         
  428.         default:
  429.             SysBeep(10);
  430.             break;
  431.     }
  432.     
  433.     // Set the new pattern
  434.     GXIgnoreGraphicsNotice(pattern_already_set);
  435.     GXSetShapePattern(gGridShape, &gPattern);
  436.     GXPopGraphicsNotice();
  437.     
  438.     // Draw it offscreen
  439.     DrawOff();
  440. } // RemakeTile
  441.  
  442. // Change op shapes and dragger as necessary to fit new symmetry
  443. void ChangeSymmetry(long oldSym, long newSym)
  444. {
  445.     RemakeOpShapes(false, newSym);
  446.     gSymmetry = newSym;
  447.     switch(gSymmetry)
  448.     {
  449.         case p1:
  450.             p1_ChangeLattice();
  451.             break;
  452.  
  453.         case pg:
  454.             pg_ChangeLattice();
  455.             break;
  456.         
  457.         case pm:
  458.             pm_ChangeLattice();
  459.             break;
  460.  
  461.         case cm:
  462.             cm_ChangeLattice();
  463.             break;
  464.  
  465.         case p2:
  466.             p2_ChangeLattice();
  467.             break;
  468.  
  469.         default:
  470.             break;
  471.     }
  472.     if(gKeepClosed)
  473.         AttachConstraints();
  474. }
  475.                     
  476.  
  477.  
  478. //-------------------------------------------------------------
  479. //    Drawing routines    
  480.  
  481. // Draws everything
  482. void TileUpdate(void)
  483. {
  484.     // Transfer grid from offscreen
  485.     GXDrawShape(gOffScreen.draw);
  486.  
  487.      // Draw the ops shapes
  488.      DrawOpShapes();
  489.  
  490.      // Draw the draggable bit
  491.      DrawDraggable();
  492. }
  493.  
  494. void DrawOff(void)
  495. {
  496.     GXDrawShape(gBGShape);
  497.       GXDrawShape(gGridShape);
  498. }
  499.  
  500. void OffToScreen(void)
  501. {
  502.     GXDrawShape(gOffScreen.draw);
  503. }
  504.           
  505. void DrawDraggable(void)
  506. {
  507.      GXDrawShape(gDragger.snake1);
  508. }
  509.  
  510. void EraseDraggable(void)
  511. {
  512.     EraseAShape(gDragger.snake1);
  513. }
  514.  
  515. void DrawOpShapes(void)
  516. {
  517.     short    cnt = kNumOpShapes;
  518.     
  519.     while(--cnt >= 0)
  520.         if(gOpShapes[cnt] != nil)
  521.             GXDrawShape(gOpShapes[cnt]);
  522. }
  523.  
  524. void EraseXShape(gxShape theShape);
  525.  
  526. // Erases by drawing it in the BG color
  527. void EraseAShape(gxShape theShape)
  528. {
  529.     commonColor    savedColor;
  530.     
  531.     GXIgnoreGraphicsNotice(color_already_set);
  532.     
  533.     savedColor = GetShapeCommonColor(theShape);
  534.     SetShapeCommonColor(theShape, kBGColor);
  535.     GXDrawShape(theShape);
  536.     SetShapeCommonColor(theShape, savedColor);
  537.  
  538.     GXPopGraphicsNotice();
  539. }
  540.  
  541. // Erases by copying from the offscreen
  542. void EraseXShape(gxShape theShape)
  543. {
  544.     gxShape        savedClip, tempShape;
  545.     gxRectangle    shapeBounds;
  546.     
  547.      tempShape = GXCopyToShape(nil, theShape);
  548.     GXPrimitiveShape(tempShape);
  549.     GXGetShapeBounds(tempShape, 0, &shapeBounds);
  550.     GXDisposeShape(tempShape);
  551.     tempShape = GXNewRectangle(&shapeBounds);
  552.     savedClip = GXGetShapeClip(gOffScreen.draw);
  553.     GXSetShapeClip(gOffScreen.draw, tempShape);
  554.     GXDisposeShape(tempShape);
  555.     GXDrawShape(gOffScreen.draw);
  556.     GXSetShapeClip(gOffScreen.draw, savedClip);
  557.     GXDisposeShape(savedClip);
  558. }
  559.  
  560. void DisposeDragger(void)
  561. {
  562.     GXDisposeShape(gDragger.snake1);
  563. }
  564.  
  565. void DisposeOpShapes(void)
  566. {
  567.     short    cnt = kNumOpShapes;
  568.     
  569.     while(--cnt >= 0)
  570.         if(gOpShapes[cnt] != nil)
  571.         {
  572.             GXDisposeShape(gOpShapes[cnt]);
  573.             gOpShapes[cnt] = nil;
  574.         }
  575. }
  576.  
  577. //-------------------------------------------------------------
  578. //    From Luke    
  579.  
  580. gxShape  GetWindowBoundsShape(WindowPtr wind)
  581. {
  582.     Rect            theRect;
  583.     Point            QDtopLeft;
  584.     Point            QDbotRight;
  585.     gxPoint            QD2topLeft;
  586.     gxPoint            QD2botRight;
  587.     gxRectangle        theQD2Rect;
  588.     
  589.     /**  The QuickDraw rect and points which represent the portRect of the window.  **/
  590.     theRect = wind->portRect;
  591.     QDtopLeft.h = theRect.left;
  592.     QDtopLeft.v = theRect.top;
  593.     QDbotRight.h = theRect.right;
  594.     QDbotRight.v = theRect.bottom;
  595.     
  596.     /**  Convert the global Quickdraw coordinates to local fixed coordinates.  **/
  597.     GXQDGlobalToFixedLocal(&QDtopLeft, &QD2topLeft);
  598.     GXQDGlobalToFixedLocal(&QDbotRight, &QD2botRight);
  599.  
  600.     /**  Setup the dimensions for "gBGShape" **/ 
  601.     theQD2Rect.top = QD2topLeft.y;
  602.     theQD2Rect.left = QD2topLeft.x;
  603.     theQD2Rect.bottom = QD2botRight.y;
  604.     theQD2Rect.right = QD2botRight.x;
  605.     
  606.     return (GXNewRectangle(&theQD2Rect));
  607. }
  608.  
  609. Boolean ChangeWindowSize(WindowPtr wind, short hSize, short vSize)
  610. {
  611.     gxBitmap        offMap;
  612.     gxShape        offBits;
  613.     gxRectangle    windRect;
  614.     
  615.     // Try to remake the off-screen bitmap. !!! I'm sure there's a better way to do this,
  616.     // just resizing the appropriate bitmap shape, whichever one it is.
  617.     offMap.image = nil;
  618.     offMap.width = hSize;
  619.     offMap.height = vSize;
  620.     offMap.rowBytes = 0;
  621.     offMap.pixelSize = 8;
  622.     offMap.space = gxIndexedSpace;
  623.     offMap.set = gColors;
  624.     offMap.profile = nil;
  625.     offBits = GXNewBitmap(&offMap, &gOrigin);
  626.     if(offBits <= (gxShape)nil)
  627.         return false;
  628.     
  629.     // Kill old one and make new one
  630.     DisposeOffscreen(&gOffScreen);
  631.     CreateOffscreen(&gOffScreen, offBits);
  632.     GXDisposeShape(offBits);
  633.     
  634.     // Size the window
  635.     SizeWindow(wind, hSize, vSize, true);
  636.     ClipRect(&wind->portRect);
  637.     
  638.     // Update other 2 rect shapes
  639.     ShortRectToFixed(&wind->portRect, &windRect);
  640.     GXSetRectangle(gBGShape, &windRect);
  641.     GXSetRectangle(gGridShape, &windRect);
  642.     
  643.     // Set them to draw in new offscreen
  644.     GXSetShapeTransform(gBGShape, gOffScreen.xform);
  645.     GXSetShapeTransform(gGridShape, gOffScreen.xform);
  646.     
  647.     // Update the offscreen
  648.     DrawOff();
  649.     
  650.     return true;
  651. }
  652.  
  653. void SetLinePoints(gxShape theLine, gxPoint *start, gxPoint *end)
  654. {
  655.     gxLine    newGeo;
  656.     
  657.     newGeo.first = *start;
  658.     newGeo.last = *end;
  659.     GXSetLine(theLine, &newGeo);
  660. }